package model

import (
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)

//func QuerySalesByUserId(userId interface{}) []OrderSale {
//	var res []OrderSale
//	GlobalDB.Where("saler_id = ?", userId).Find(&res)
//	return res
//}
//func QueryBuysByUserId(userId interface{}) []OrderBuy {
//	var res []OrderBuy
//	GlobalDB.Where("saler_id = ?", userId).Find(&res)
//	return res
//}
//func QueryDealsByUserId(userId interface{}) []Deal {
//	var res []Deal
//	GlobalDB.Where("saler_id = ?", userId).Or("buyer_id = ?", userId).Find(&res)
//	return res
//}

func QueryItemCount(stat string, model interface{}) (int64, error) {
	var count int64
	err := GlobalDB.Model(model).Where("state = ?", stat).Count(&count).Error
	return count, err
}

func QueryItemsBase(stat, isPart string, offset, num int, res interface{}, sm *gorm.DB) {
	if isPart == "0" {
		sm.Where("state = ?", stat).Order(clause.OrderByColumn{
			Column: clause.Column{
				Name: "created",
			},
			Desc: false,
		}).Find(res)
	} else {
		sm.Where("state = ?", stat).Order(clause.OrderByColumn{
			Column: clause.Column{
				Name: "created",
			},
			Desc: false,
		}).Offset(offset).Limit(num).Find(res)
	}
}

func QueryItems(stat, isPart string, offset, num int, res interface{}) {
	QueryItemsBase(stat, isPart, offset, num, res, GlobalDB)
}

func QuerySalesByUserId(stat, isPart string, offset, num int, res interface{}, userId interface{}) {
	QueryItemsBase(stat, isPart, offset, num, res, GlobalDB.Where("saler_id = ?", userId))
}
func QueryBuysByUserId(stat, isPart string, offset, num int, res interface{}, userId interface{}) {
	QueryItemsBase(stat, isPart, offset, num, res, GlobalDB.Where("buyer_id = ?", userId))
}
func QueryDealsByUserId(stat, isPart string, offset, num int, res interface{}, userId interface{}) {
	QueryItemsBase(stat, isPart, offset, num, res, GlobalDB.Where("saler_id = ? or buyer_id = ?", userId, userId))
}

func QuerySaleByUserIdOrNot(saleId string, userId int64) *OrderSale {
	res := OrderSale{}
	if userId != -1 {
		GlobalDB.Where("sale_id = ? and saler_id = ?", saleId, userId).First(&res)
		return &res
	}
	GlobalDB.Where("sale_id = ?", saleId).First(&res)
	return &res
}
func QueryBuyByUserIdOrNot(buyId string, userId int64) *OrderBuy {
	res := OrderBuy{}
	if userId != -1 {
		GlobalDB.Where("buy_id = ? and buyer_id = ?", buyId, userId).First(&res)
		return &res
	}
	GlobalDB.Where("buy_id = ?", buyId).First(&res)
	return &res
}
func QueryDealByUserIdOrNot(dealId string, userId int64) *Deal {
	res := Deal{}
	if userId != -1 {
		GlobalDB.Where("deal_id = ? and (buyer_id = ? or saler_id = ?)", dealId, userId, userId).First(&res)
		return &res
	}
	GlobalDB.Where("deal_id = ?", dealId).First(&res)
	return &res
}
func QueryDealBySalerIdOrNot(dealId string, salerId int64) *Deal {
	res := Deal{}
	if salerId != -1 {
		GlobalDB.Where("deal_id = ? and saler_id = ?", dealId, salerId).First(&res)
		return &res
	}
	GlobalDB.Where("deal_id = ?", dealId).First(&res)
	return &res
}

func QuerySaleBySaleId(saleId int64) *OrderSale {
	res := OrderSale{}
	GlobalDB.First(&res, "sale_id = ?", saleId)
	return &res
}
func QueryBuyByBuyId(buyId int64) *OrderBuy {
	res := OrderBuy{}
	GlobalDB.First(&res, "buy_id = ?", buyId)
	return &res
}
func QueryDealByDealId(dealId int64) *Deal {
	res := Deal{}
	GlobalDB.First(&res, "deal_id = ?", dealId)
	return &res
}

func UpdateItem(item interface{}) error {
	return GlobalDB.Updates(item).Error
}

// 交界处理
func QueryMixedBuy(goodId, minPrice, totalNum, userId int64) (leftNum int64, dealList []int64) {

	defer func() {
		r := recover()
		if r == nil {
			return
		}
		if err, ok := r.(error); ok {
			panic(err)
		}
	}()

	leftNum = totalNum

	for totalNum > 0 {
		buyInfo := OrderBuy{}
		ctx := GlobalDB.Begin()

		ctx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("good_id = ? and max_price >= ? and left_num > ? and state = ?", goodId, minPrice, 0, "ready").
			Order(clause.OrderByColumn{Column: clause.Column{Name: "max_price"}, Desc: true}).
			First(&buyInfo)

		if buyInfo.State != "ready" {
			ctx.Rollback()
			break
		}

		if buyInfo.LeftNum >= totalNum {
			// 创建 交易单
			deal := Deal{
				GoodId:   goodId,
				BuyerId:  buyInfo.BuyerId,
				SalerId:  userId,
				Num:      totalNum,
				DealCost: buyInfo.MaxPrice,
				Exp:      time.Now().UnixMilli() + 24*60*60*1000,
				State:    "ready",
			}
			err := ctx.Create(&deal).Error
			if err != nil {
				ctx.Rollback()
				return
			}
			dealList = append(dealList, deal.DealId)

			// 更新 求购单
			err = ctx.Where("buy_id = ?", buyInfo.BuyId).Updates(&OrderBuy{
				LeftNum: buyInfo.LeftNum - totalNum,
			}).Error
			if err != nil {
				ctx.Rollback()
				return
			}
			leftNum = 0
			ctx.Commit()
			return
		}
		// 创建 交易单
		deal := Deal{
			GoodId:   goodId,
			BuyerId:  buyInfo.BuyerId,
			SalerId:  userId,
			Num:      buyInfo.LeftNum,
			DealCost: buyInfo.MaxPrice,
			Exp:      time.Now().UnixMilli() + 24*60*60*1000,
			State:    "ready",
		}
		err := ctx.Create(&deal).Error
		if err != nil {
			ctx.Rollback()
			continue
		}
		dealList = append(dealList, deal.DealId)

		// 更新 求购单
		err = ctx.Where("buy_id = ?", buyInfo.BuyId).Updates(&OrderBuy{
			LeftNum: 0,
			State:   "closed",
		}).Error
		if err != nil {
			ctx.Rollback()
			continue
		}
		leftNum = totalNum - buyInfo.LeftNum

		ctx.Commit()
	}

	return
}

func InsertSale(sale *OrderSale) error {
	return GlobalDB.Create(sale).Error
}

func QueryMixedSale(goodId, maxPrice, totalNum, userId, accountId int64) (leftNum, refund int64, dealList []int64) {

	defer func() {
		r := recover()
		if r == nil {
			return
		}
		if err, ok := r.(error); ok {
			panic(err)
		}
	}()

	leftNum = totalNum
	refund = 0

	for totalNum > 0 {
		saleInfo := OrderSale{}
		ctx := GlobalDB.Begin()

		ctx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("good_id = ? and min_price <= ? and left_num > ? and state = ?", goodId, maxPrice, 0, "ready").
			Order(clause.OrderByColumn{Column: clause.Column{Name: "min_price"}, Desc: true}).
			First(&saleInfo)

		if saleInfo.State != "ready" {
			ctx.Rollback()
			break
		}

		if saleInfo.LeftNum >= totalNum {
			// 创建 交易单
			deal := Deal{
				GoodId:    goodId,
				BuyerId:   userId,
				SalerId:   saleInfo.SalerId,
				Num:       totalNum,
				DealCost:  saleInfo.MinPrice,
				Exp:       time.Now().UnixMilli() + 24*60*60*1000,
				State:     "ready",
				AccountId: accountId,
			}
			err := ctx.Create(&deal).Error
			if err != nil {
				ctx.Rollback()
				return
			}
			dealList = append(dealList, deal.DealId)

			// 更新 供应单
			err = ctx.Where("sale_id = ?", saleInfo.SaleId).Updates(&OrderSale{
				LeftNum: saleInfo.LeftNum - totalNum,
			}).Error
			if err != nil {
				ctx.Rollback()
				return
			}
			leftNum = 0
			refund = (maxPrice - saleInfo.MinPrice) * totalNum
			ctx.Commit()
			return
		}
		// 创建 交易单
		deal := Deal{
			GoodId:    goodId,
			BuyerId:   userId,
			SalerId:   saleInfo.SalerId,
			Num:       saleInfo.LeftNum,
			DealCost:  saleInfo.MinPrice,
			Exp:       time.Now().UnixMilli() + 24*60*60*1000,
			State:     "ready",
			AccountId: accountId,
		}
		err := ctx.Create(&deal).Error
		if err != nil {
			ctx.Rollback()
			continue
		}
		dealList = append(dealList, deal.DealId)

		// 更新 供应单
		err = ctx.Where("sale_id = ?", saleInfo.SaleId).Updates(&OrderSale{
			LeftNum: 0,
			State:   "closed",
		}).Error
		if err != nil {
			ctx.Rollback()
			continue
		}
		leftNum = totalNum - saleInfo.LeftNum
		refund += (maxPrice - saleInfo.MinPrice) * totalNum

		ctx.Commit()
	}

	return
}

func InsertBuy(buy *OrderBuy) error {
	return GlobalDB.Create(buy).Error
}

func UpdateSaleByUserIdAndSaleId(userId int64, saleId, state string) error {
	res := OrderSale{}
	GlobalDB.Where("saler_id = ? and sale_id = ?", userId, saleId).First(&res)
	if res.State != "ready" {
		return errors.New("[GORM] Nil result")
	}
	return GlobalDB.Where("sale_id = ?", saleId).Updates(&OrderSale{State: state}).Error
}

// 需要二次验证
func UpdateBuyByUserIdAndBuyId(userId int64, buyId, state string) (int64, error) {
	res := OrderBuy{}
	GlobalDB.Where("buyer_id = ? and buy_id = ?", userId, buyId).First(&res)
	if res.State != "ready" {
		return 0, errors.New("[GORM] Nil result")
	}
	return res.LeftNum * res.MaxPrice, GlobalDB.Where("buy_id = ?", res.BuyId).Updates(&OrderBuy{State: state}).Error
}

func UpdateDealByUserIdAndDealId(userId int64, dealId, state string) (refund int64, buyerId int64, err error) {
	res := Deal{}
	GlobalDB.Where("(buyer_id = ? or saler_id = ?) and deal_id = ?", userId, userId, dealId).First(&res)
	if res.State != "ready" {
		return 0, -1, errors.New("[GORM] Nil result")
	}
	return res.Num * res.DealCost, res.BuyerId, GlobalDB.Where("deal_id = ?", dealId).Updates(&Deal{State: state}).Error
}

func UpdateDealByDealId(dealId, state string) (totalIncome int64, buyerId int64, err error) {
	res := Deal{}
	GlobalDB.Where("deal_id = ?", dealId).First(&res)
	if res.State != "ready" {
		return 0, -1, errors.New("[GORM] Nil result")
	}
	return res.Num * res.DealCost, res.BuyerId, GlobalDB.Where("deal_id = ?", dealId).Updates(&Deal{State: state}).Error
}
